﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using MoMoTransferVN_WP.momo.transporter;
using com.mservice.momo.msg;
using MoMoTransferVN_WP.utils;
using ProtoBuf;
using System.IO;
using System.Xml.Serialization;
using SocketEx;
using ProtoBuf.Meta;
using System.Text;
using SQLite;
using Windows.Storage;
using Microsoft.Phone.Scheduler;
using System.Collections.ObjectModel;
using Microsoft.Phone.Net.NetworkInformation;
using MoMoTransferVN_WP.database;
using System.IO.IsolatedStorage;

namespace MoMoTransferVN_WP
{
    public partial class App : Application, MoMoTcpClient.IConnectStatus
    {
        /// <summary>
        /// Provides easy access to the root frame of the Phone Application.
        /// </summary>
        /// <returns>The root frame of the Phone Application.</returns>
        public PhoneApplicationFrame RootFrame { get; private set; }
        /// <summary>
        /// Gets or sets the connection.
        /// </summary>
        /// <value>
        /// The connection.
        /// </value>
        public static SQLiteAsyncConnection Connection { get; set; }
        /// <summary>
        /// Constructor for the Application object.
        /// </summary>
        public App()
        {
            // Global handler for uncaught exceptions. 
            UnhandledException += Application_UnhandledException;

            // Standard Silverlight initialization
            InitializeComponent();

            // Phone-specific initialization
            InitializePhoneApplication();

            // Show graphics profiling information while debugging.
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // Display the current frame rate counters.
                Application.Current.Host.Settings.EnableFrameRateCounter = true;

                // Show the areas of the app that are being redrawn in each frame.
                //Application.Current.Host.Settings.EnableRedrawRegions = true;

                // Enable non-production analysis visualization mode, 
                // which shows areas of a page that are handed off to GPU with a colored overlay.
                //Application.Current.Host.Settings.EnableCacheVisualization = true;

                // Disable the application idle detection by setting the UserIdleDetectionMode property of the
                // application's PhoneApplicationService object to Disabled.
                // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
                // and consume battery power when the user is not using the phone.
                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            }  
        }

        // Code to execute when the application is launching (eg, from Start)
        // This code will not execute when the application is reactivated
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {

            listen();
            saveDBasync();
            LogUtils.f("Application_Launching");

            mMoMoTcpClient = new MoMoTcpClient((MoMoTcpClient.IConnectStatus)this);

            

            //find periodic task, check if it exist
            periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
            if (periodicTask != null)
            {
                removeAgent(periodicTaskName);
            }

            // create periodic task
            periodicTask = new PeriodicTask(periodicTaskName);

            // The description is required for periodic agents. This is the string that the user
            // will see in the background services Settings page on the device.
            periodicTask.Description = "This demonstrates a periodic task.";

            // Place the call to Add in a try block in case the user has disabled agents.
            try
            {
                ScheduledActionService.Add(periodicTask);

                // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
                #if(DEBUG_AGENT)
                ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60));
                #endif
            }
            catch (InvalidOperationException exception)
            {
                if (exception.Message.Contains("BNS Error: The action is disabled"))
                {
                    MessageBox.Show("Background agents for this application have been disabled by the user.");
                }

                if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
                {
                    // No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
                }
            }
            catch (SchedulerServiceException)
            {
                // No user action required.  
            }

        }
        private async void saveDBasync()
        {
            StorageFile dbFile = null;
            try
            {
                // Try to get the 
                dbFile = await StorageFile.GetFileFromPathAsync(DBHelper.DB_PATH);
            }
            catch (FileNotFoundException)
            {
                if (dbFile == null)
                {
                    // Copy file from installation folder to local folder.
                    // Obtain the virtual store for the application.
                    IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication();

                    // Create a stream for the file in the installation folder.
                    using (Stream input = Application.GetResourceStream(new Uri("momoer.sqlite", UriKind.Relative)).Stream)
                    {
                        // Create a stream for the new file in the local folder.
                        using (IsolatedStorageFileStream output = iso.CreateFile(DBHelper.DB_PATH))
                        {
                            // Initialize the buffer.
                            byte[] readBuffer = new byte[4096];
                            int bytesRead = -1;

                            // Copy the file from the installation folder to the local folder. 
                            while ((bytesRead = input.Read(readBuffer, 0, readBuffer.Length)) > 0)
                            {
                                output.Write(readBuffer, 0, bytesRead);
                            }
                        }
                    }
                }
            }
        }
        PeriodicTask periodicTask;
        string periodicTaskName = "MoMoPeriodicAgent";

        private void removeAgent(string name)
        {
            try
            {
                ScheduledActionService.Remove(name);
            }
            catch (Exception)
            {
            }
        }


        // Code to execute when the application is activated (brought to foreground)
        // This code will not execute when the application is first launched
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
            LogUtils.f("Application_Activated");
            if (mMoMoTcpClient == null)
            {
                mMoMoTcpClient = new MoMoTcpClient((MoMoTcpClient.IConnectStatus)this);
            }
        }

        // Code to execute when the application is deactivated (sent to background)
        // This code will not execute when the application is closing
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
            LogUtils.f("Application_Deactivated");
            if(mMoMoTcpClient != null){
                mMoMoTcpClient.forceStop();
                mMoMoTcpClient = null;
            }
                
        }

        // Code to execute when the application is closing (eg, user hit Back)
        // This code will not execute when the application is deactivated
        private void Application_Closing(object sender, ClosingEventArgs e)
        {
            LogUtils.f("Application_Closing");

        }

        // Code to execute if a navigation fails
        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // A navigation has failed; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        // Code to execute on Unhandled Exceptions
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // An unhandled exception has occurred; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        #region Phone application initialization

        // Avoid double-initialization
        private bool phoneApplicationInitialized = false;

        // Do not add any additional code to this method
        private void InitializePhoneApplication()
        {
            if (phoneApplicationInitialized)
                return;

            // Create the frame but don't set it as RootVisual yet; this allows the splash
            // screen to remain active until the application is ready to render.
            RootFrame = new PhoneApplicationFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;

            // Handle navigation failures
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;

            // Ensure we don't initialize again
            phoneApplicationInitialized = true;
        }

        // Do not add any additional code to this method
        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
        {
            // Set the root visual to allow the application to render
            if (RootVisual != RootFrame)
                RootVisual = RootFrame;

            // Remove this handler since it is no longer needed
            RootFrame.Navigated -= CompleteInitializePhoneApplication;
        }

        #endregion

        //todo @luatle luat khai bao cac bien tai day
        public MoMoTcpClient mMoMoTcpClient;
        public static string mSessionKey = "";
        public static long TIME_DELTA = 0;

        public static long getNextMsgId()
        {
            return getCurrentMiliseconds() - TIME_DELTA;
        }

        public static long getCurrentMiliseconds()
        {
            return (long)DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
        }

        void MoMoTcpClient.IConnectStatus.onConnected()
        {
            MoMoTransferVN_WP.utils.LogUtils.f("onConnected");
        }

        void MoMoTcpClient.IConnectStatus.onDisconnectWithError(string error)
        {
            MoMoTransferVN_WP.utils.LogUtils.f("onDisconnect with " + error);
            if (ckb != null)
            {
                ckb.onDisconnectWithError(error);
            }
        }

        void MoMoTcpClient.IConnectStatus.onMessageCome(MomoMessage msg)
        {
            MoMoTransferVN_WP.utils.LogUtils.f(String.Format("onMessageCome: --type: {0}--index: {1}", msg.cmdType, msg.cmdIndex));

            if (ckb != null)
            {
                ckb.onMoMoMessageCome(msg.cmdType, msg.cmdIndex, msg.cmdBody);
            }
            else
            {
                LogUtils.f("check callback");
            }

            if (msg.cmdType == 2)
            {
                StandardReply reply = StandardReply.CreateBuilder().MergeFrom(msg.cmdBody).Build();
                if (reply != null)
                {
                    LogUtils.f("rCode = " + reply.Rcode);
                    switch ((int) reply.Rcode)
                    {
                        case (int) SystemError.SERVICE_IS_PAUSED:
                            LogUtils.f("Dich vụ dung hoat dong");
                            break;
                        case (int) SystemError.ALL_OK:
                            LogUtils.f("No info");
                            break;
                        case (int) SystemError.MSG_FORMAT_NOT_CORRECT:
                            LogUtils.f("Message gui len khong dung dinh dang");     
                            break;
                        case (int) SystemError.NOT_HELLO_YET:
                            LogUtils.f("Chua hello den server, can phai hello lai");
                            break;
                        case (int) SystemError.NUMBER_NOT_VALID:
                            LogUtils.f("So dien thoai khong hop le");
                            break;
                        case (int) SystemError.SYSTEM_ERROR:
                            LogUtils.f("Loi he thong");
                            break;
                        case (int) SystemError.NOT_SET_UP:
                            LogUtils.f("Chua setup");
                            break;
                        case (int) SystemError.NUMBER_EXISTED:
                            LogUtils.f("So dien thoai da ton tai");
                            break;
                        case (int) SystemError.OLD_PIN_NOT_CORRECT:
                            LogUtils.f("Ma Pin cu khong chinh xac");
                            break;
                        case (int) SystemError.IS_BLOCKING:
                            LogUtils.f("Tai khoan da bi khoa");
                            break;
                    }
                }
            }else if (msg.cmdType == (Int32)MsgType.HELLO_REPLY)
            {
                LogUtils.f("Hello reply");

                //Create another builder, merge the byte[], and build the message:
                HelloReply reply = HelloReply.CreateBuilder().MergeFrom(msg.cmdBody).Build();

                if (reply != null)
                {
                    RegStatus reg = reply.RegStatus;
                    LogUtils.f("isSetup = " + reg.IsSetup);
                    LogUtils.f("isRegister = " + reg.IsReged);
                    LogUtils.f("isNamed = " + reg.IsNamed);
                    LogUtils.f("isActive = " + reg.IsActive);
                    LogUtils.f("isFrozen = " + reg.IsFrozen);
                    LogUtils.f("isStopped = " + reg.IsStopped);
                    LogUtils.f("isSuppend = " + reg.IsSuppend);

                    //user status
                    SettingUtils.saveSetting(Assets.KEY_USER_SETUP, reg.IsSetup);
                    SettingUtils.saveSetting(Assets.KEY_USER_NAMED, reg.IsNamed);
                    SettingUtils.saveSetting(Assets.KEY_USER_REGISTER, reg.IsReged);

                    //lastest version
                    SettingUtils.saveSetting(Assets.KEY_CURRENT_CODE_IN_SERVER, reply.VersionCode);
                    SettingUtils.saveSetting(Assets.KEY_CURRENT_VERSION_IN_SERVER, reply.VersionName);

                    //todo send devices info
                    if (!SettingUtils.readBool(Assets.KEY_SEND_DEVICES_INFO))
                    {
                        MomoMessage deviceInfoMsg = MoMoReq.processSendDevicesInfo();
                        mMoMoTcpClient.sendMoMoMesage(deviceInfoMsg);
                        //mMoMoTcpClient.sendMoMoMesage(MoMoReq.processGetOtp());
                    }

                    //todo get services

                    bool mIsLogin = false;
                    switch (reply.Rcode)
                    {
                        case (int)HelloReply.Types.ResultCode.ALL_OK:
                            LogUtils.f("Hello thanh cong den server");
                            break;
                        case (int)HelloReply.Types.ResultCode.PHONE_NOT_CORRECT:
                            LogUtils.f("Truong hop hello den server co so phone ko hop le");
                            break;
                        case (int)HelloReply.Types.ResultCode.LOG_ON:
                            LogUtils.f("Truong hop hello khi da dang nhap");
                            mIsLogin = true;
                            break;
                        case (int)HelloReply.Types.ResultCode.LOG_ON_ANOTHER_DEVICE:
                            LogUtils.f("Truong hop dang nhap tren thiet bi khac");
                            //todo remove all data on this phone and move to setup page
                            break;
                        case (int)HelloReply.Types.ResultCode.NOT_LOG_ON:
                            LogUtils.f("Truong hop hello khi chua dang nhap");
                            break;
                        default:
                            break;
                    }
                    SettingUtils.saveSetting(Assets.KEY_USER_LOGIN, mIsLogin);
                }
            }
            else if (msg.cmdType == (int)MsgType.LOGIN_REPLY)
            {
                LogInReply reply = LogInReply.CreateBuilder().MergeFrom(msg.cmdBody).Build();
                if (reply != null)
                {
                    TIME_DELTA = getCurrentMiliseconds() - (long)reply.Time;

                    App.mSessionKey = reply.Skey;

                }
            }
            else if (msg.cmdType == (int)MsgType.DEVICE_INFO_REPLY)
            {
                SettingUtils.saveSetting(Assets.KEY_SEND_DEVICES_INFO, true);
            }
        }

        IMoMoMessageCome ckb;
        public void setCkb(IMoMoMessageCome ckb)
        {
            this.ckb = ckb;
        }

        public interface IMoMoMessageCome
        {
            void onMoMoMessageCome(int cmdType, long cmdIndex, byte[] cmdBody);

            void onDisconnectWithError(string error);
        }


        // List of all changes detected while the app is running.
        public ObservableCollection<string> Changes { get; set; }

        // List of all currently available network interfaces
        public ObservableCollection<string> NetworkInterfaces { get; set; }

        public void listen()
        {
            // Initialise the Changes list.
            Changes = new ObservableCollection<string>();

            // Bind the ListBox to the Changes list
            //lbNetworkChanges.DataContext = Changes;

            NetworkInterfaces = new ObservableCollection<string>();
            //lbNetworkInterfaces.DataContext = NetworkInterfaces;

            // Subscribe to the NetworkAvailabilityChanged event
            DeviceNetworkInformation.NetworkAvailabilityChanged += new EventHandler<NetworkNotificationEventArgs>(ChangeDetected);

        }

        // In this callback, we examine the change that was detected. In this example, we are 
        // creating a simple information string and adding that to the event list on the UI. 
        // In a real application, this is where you might adjust your communication connection 
        // in order to take advantage of a network availability change. 
        void ChangeDetected(object sender, NetworkNotificationEventArgs e)
        {
            string change = string.Empty;
            switch (e.NotificationType)
            {
                case NetworkNotificationType.InterfaceConnected:
                    change = "Connected to ";
                    break;
                case NetworkNotificationType.InterfaceDisconnected:
                    change = "Disconnected from ";
                    break;
                case NetworkNotificationType.CharacteristicUpdate:
                    change = "Characteristics changed for ";
                    break;
                default:
                    change = "Unknown change with ";
                    break;
            }

            string changeInformation = String.Format(" {0} {1} {2} ({3})",
                        DateTime.Now.ToString(), change, e.NetworkInterface.InterfaceName,
                        e.NetworkInterface.InterfaceType.ToString());
                
            LogUtils.f("changeInfomation " + changeInformation);

        }

    }
}